﻿using System;
using System.Runtime.InteropServices;
using System.Text;

namespace AD域工具.Tool
{
    class API
    {
        #region 计算机名称类型
        /// <summary>
        /// 计算机名称类型
        /// </summary>
        #endregion
        public enum COMPUTER_NAME_FORMAT
        {
            /// <summary>
            /// 本地计算机的NetBIOS名称。如果本地计算机是群集中的节点，则lpBuffer会接收群集虚拟服务器的NetBIOS名称。
            /// </summary>
            ComputerNameNetBIOS,
            /// <summary>
            /// 本地计算机的DNS主机名。如果本地计算机是群集中的节点，则lpBuffer会接收群集虚拟服务器的DNS主机名。
            /// </summary>
            ComputerNameDnsHostname,
            /// <summary>
            /// 分配给本地计算机的DNS域的名称。如果本地计算机是群集中的节点，则lpBuffer会接收群集虚拟服务器的DNS域名。
            /// </summary>
            ComputerNameDnsDomain,
            /// <summary>
            /// 唯一标识本地计算机的标准DNS名称。该名称是DNS主机名和DNS域名的组合，格式为HostName。域名。如果本地计算机是群集中的节点，则lpBuffer会接收群集虚拟服务器的标准DNS名称。
            /// </summary>
            ComputerNameDnsFullyQualified,
            /// <summary>
            /// 本地计算机的NetBIOS名称。如果本地计算机是群集中的节点，则lpBuffer会接收本地计算机的NetBIOS名称，而不是群集虚拟服务器的名称。
            /// </summary>
            ComputerNamePhysicalNetBIOS,
            /// <summary>
            /// 本地计算机的DNS主机名。如果本地计算机是群集中的节点，则lpBuffer会接收本地计算机的DNS主机名，而不是群集虚拟服务器的名称。
            /// </summary>
            ComputerNamePhysicalDnsHostname,
            /// <summary>
            /// 分配给本地计算机的DNS域的名称。如果本地计算机是群集中的节点，则lpBuffer会接收本地计算机的DNS域名，而不是群集虚拟服务器的名称。
            /// </summary>
            ComputerNamePhysicalDnsDomain,
            /// <summary>
            /// 唯一标识计算机的标准DNS名称。如果本地计算机是群集中的节点，则lpBuffer会接收本地计算机的标准DNS名称，而不是群集虚拟服务器的名称。
            /// 完全限定的DNS名称是DNS主机名和DNS域名的组合，格式为HostName。域名。
            /// </summary>
            ComputerNamePhysicalDnsFullyQualified
        }

        #region kernel32 SetComputerNameEx
        [DllImport("kernel32.dll")]
        public static extern bool SetComputerNameEx(COMPUTER_NAME_FORMAT type, string lpComputerName);
        #endregion

        #region kernel32 GetComputerNameEx
        [DllImport("kernel32.dll")]
        public static extern bool GetComputerNameEx(COMPUTER_NAME_FORMAT NameType, StringBuilder lpBuffer, ref uint lpnSize);
        #endregion

        #region 取计算机名称
        /// <summary>
        /// 取计算机名称
        /// </summary>
        /// <param name="type">计算机名称类型</param>
        /// <param name="size">获取名字字符串的长度</param>
        /// <returns></returns>
        #endregion
        public static string GetComputerNameEx(COMPUTER_NAME_FORMAT type, uint size = 100)
        {
            StringBuilder name = new StringBuilder((int)size);
            bool success = GetComputerNameEx(type, name, ref size);
            if (success)
            {
                return name.ToString();
            }
            else
            {
                return null;
            }
        }

        #region 加域控制选项参数
        /// <summary>
        /// 加域控制选项参数
        /// </summary>
        [Flags]
        #endregion
        public enum JoinDomainOptions
        {
            /// <summary>
            /// 将计算机加入域。如果未指定此值，则将计算机加入工作组。
            /// </summary>
            NETSETUP_JOIN_DOMAIN = 0x00000001,
            /// <summary>
            /// 在域上创建帐户。
            /// </summary>
            NETSETUP_ACCT_CREATE = 0x00000002,
            /// <summary>
            /// 在域上删除帐户.
            /// </summary>
            NETSETUP_ACCT_DELETE = 0x00000004,
            /// <summary>
            /// 加入操作是升级的一部分。
            /// </summary>
            NETSETUP_WIN9X_UPGRADE = 0x00000010,
            /// <summary>
            /// 即使计算机已经加入域，也允许加入新域。
            /// </summary>
            NETSETUP_DOMAIN_JOIN_IF_JOINED = 0x00000020,
            /// <summary>
            /// 执行不安全的联接。
            /// </summary>
            NETSETUP_JOIN_UNSECURE = 0x00000040,
            /// <summary>
            /// 指示lpPassword参数指定本地计算机帐户密码，而不是用户密码。该标志仅对不安全的联接有效，必须通过设置NETSETUP_JOIN_UNSECURE标志来表明这一点。
            /// 如果设置此标志，则在加入操作成功后，如果该密码是有效的机器密码，则机器密码将设置为lpPassword的值。
            /// </summary>
            NETSETUP_MACHINE_PWD_PASSED = 0x00000080,
            /// <summary>
            /// 如果设置了此位，则NetJoinDomain函数将忽略无法识别的标志，并且 NetJoinDomain的行为就像未设置标志一样。
            /// </summary>
            NETSETUP_IGNORE_UNSUPPORTED_FLAGS = 0x10000000
        }

        #region netapi32 NetJoinDomain
        [DllImport("netapi32.dll", CharSet = CharSet.Unicode)]
        public static extern uint NetJoinDomain(string lpServer, string lpDomain, string lpAccountOU, string lpAccount, string lpPassword, JoinDomainOptions NameType);
        #endregion

        #region 加域控制
        /// <summary>
        /// 加域控制
        /// </summary>
        /// <param name="server">计算机的DNS或NetBIOS名称 [为空则使用本地计算机]</param>
        /// <param name="domain">域地址</param>
        /// <param name="OU">组织架构</param>
        /// <param name="account">域用户名</param>
        /// <param name="password">域密码</param>
        /// <returns></returns>
        #endregion
        public static uint NetJoinDomain(string server, string domain, string OU, string account, string password)
        {
            try
            {
                uint value1 = NetJoinDomain(server, domain, OU, account, password, JoinDomainOptions.NETSETUP_JOIN_DOMAIN | JoinDomainOptions.NETSETUP_DOMAIN_JOIN_IF_JOINED | JoinDomainOptions.NETSETUP_ACCT_CREATE);
                if (value1 == 2224)
                {
                    value1 = NetJoinDomain(null, domain, null, account, password, JoinDomainOptions.NETSETUP_JOIN_DOMAIN);
                }
                return value1;
            }
            catch (Exception error)
            {
                throw error;
            }
        }

        #region 加域控制
        /// <summary>
        /// 加域控制
        /// </summary>
        /// <param name="domain">域地址</param>
        /// <param name="account">用户名</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        #endregion
        public static string NetJoinDomain(string domain, string account, string password)
        {
            uint result = NetJoinDomain("", domain, "", account, password);
            if (result == 0)
            {
                return null;
            }
            string err = result.ToString();
            switch (err)
            {
                case "5":
                    err = "请使用管理员权限打开";
                    break;
                case "87":
                    err = "参数不正确";
                    break;
                case "110":
                    err = "系统无法打开指定的对象";
                    break;
                case "1323":
                    err = "无法更新密码";
                    break;
                case "1326":
                    err = "登录失败：用户名未知或密码错误";
                    break;
                case "1355":
                    err = "指定的域不存在或无法联系";
                    break;
                case "1780":
                    err = "空指针传递到存根";
                    break;
                case "2224":
                    err = "帐户已存在";
                    break;
                case "2691":
                    err = "计算机已加入域";
                    break;
                case "2692":
                    err = "计算机当前未加入域";
                    break;
            }
            return err;
        }
    }
}
